<!DOCTYPE html>
<html lang="zh-cn">
<head>
  
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  <title>
谈谈 iOS 中的 MVVM |
穷折腾</title>
  <link rel="stylesheet" href="/static/css/style.css" />
  <link rel="stylesheet" href="/static/css/pygments.css" />
  <link rel="alternate" type="application/rss+xml" title="RSS" href="http://blog.zorro.im/rss.xml" />
  <link rel="icon" type="image/png" href="/static/img/favicon.png">
</head>
<body>
    <div class="page-wrap">
        <section class="main-header regular">
            <div class="vertical-container">
                <div class="wrapper clearfix">
                    <header>
                        <a href="/">
                            <h1 id="main-title">穷折腾</h1>
                            <h2 id="main-subtitle">zqqf16 的个人博客</h2>
                        </a>
                    </header>
                    <nav id="main-nav" role="navigation">
                        <a href="/" class="selected">主页</a>
                        <a href="/posts/about.html" class="">关于</a>
                        <a href="https://github.com/zqqf16">Github</a>
                    </nav>
                </div>
            </div>
        </section>
        <section class="main-content">
            
<article class="main-content">
    <div class="wrapper">
        <header class="section-header">
            <h1 class="section-title">谈谈 iOS 中的 MVVM</h1>
            <span class="section-subtitle"><time datetime="2014-07-07" pubdate="">2014-07-07</time></span>
        </header>
        <section class="post-content">
        	<p>前言</p>
<p>很久没写博客了，一方面因为工作太忙，把精力都耗掉了。另一方面，由于长时间不写文章，不知道该如何下笔，越来越犹豫。</p>
<p>前几天查看了一下最近的 Google 统计，发现我的小 Blog 竟然每天都有3、5个人访问。顿时就感觉到信心大增，为了对得起观众，我决定重新拾起来。</p>
<p>今天就总结一下这一阶段 iOS 开发相关的经验。</p>
<p><em>注：鄙人搞 iOS 不到 1 年，才疏学浅，如有不对之处请指教。</em></p>
<hr />
<p>刚开始学 iOS 开发的时候，看的所有教程、实例为了简单起见，都会把应用的逻辑控制部分放在 View Controller 里。后来接手公司 iOS 项目时，里面的代码也是这样实现的，久而久之也就养成了这样的习惯。</p>
<p>随着开发的进行，代码逻辑越来越复杂，用户响应、网络交互、数据获取…等各种复杂东西都习惯性的扔到了 View Controller 里。再加上需求经常变化，为了追求稳定，通常会采用最小修改的原则，在原来设计好的逻辑下加一些“跳线”，跳来跳去~</p>
<p>这样做的后果就是代码耦合度大增，逻辑混乱。一个 View Controller 的实现文件轻轻松松达到3、4百行。</p>
<p>这样的问题大牛们早就遇到了，他们弄出了 MVVM 的概念来解决这个问题，有关 MVVM 的介绍可以参考 Objc.io 上的这篇文章 <a href="http://www.objc.io/issue-13/mvvm.html">Introduction to MVVM</a> ，大多数认都应该看过了吧~ 这里我就不再详细介绍了，只谈谈自己的理解。</p>
<p>所谓的 MVVM，就是在 View Controller 与 Model 之间加了一个叫 “View Model” 的层。把 iOS 中原本的 View 与 View Controller 合并到一个新的 “View”。</p>
<div class="codehilite"><pre>View | View Controller  &lt;--------&gt; View Model &lt;--------&gt; Model
</pre></div>


<p>新的 View 层负责所有的显示与用户交互之类的工作，而其它的，那些原本属于 View Controller 的工作全部交由 View Model 曾来处理，View 的工作能省则省。</p>
<p>例如，如果要显示用户的全名，而 Model 里却分别存储着用户的名与姓，那么把姓名合在一起的工作交给 View Model 来做，View 只负责显示。</p>
<p>再例如，如果要实现一个用户登录的功能，View 只负责把用户点击按钮的事件告诉 View Model，设置好 Delegate 或者回掉，其余的输入验证、网络交互等工作就交给 View Model 处理。</p>
<p>可见，MVVM 的中心思想就是<strong>分离</strong>。角色分离，视图与逻辑分离。通过分离，使各层之间耦合度降到最低。这样能够方便地对每个部分进行独立的单元测试，减少 Bug 的出现几率。同时也能满足需求的多变，如果分离合理，那么每次需求变化的时候需要进行的改动将会很小，不至于“牵一发而动全身”。</p>
<p>前一段时间我开发了公司的一个新客户端，采用了 MVVM 的思想。下面将将结合自己的开发经验来谈谈其中的感受。</p>
<ul>
<li>
<p>先实现功能，后谈架构</p>
<p>对于这点我有十分痛苦的领悟…… 你永远不知道下一步会添加什么需求，有时候自认为设计出了合理的架构，随着 PM 新需求的一声令下，重构。</p>
<p>这就有点像建篱笆围竹子，鬼知道竹子的根会绵延到哪。今天刚建好，感觉把竹子围住了，过两天就会发现篱笆外又冒出了竹笋……</p>
<p>当然，有经验的人在一开始就会意识到可能会出现的问题，设计合理的架构，但这种能力也是在不断的重构过程中积累的。</p>
<p>对于像我一样的初学者，个人建议先把功能实现，然后在修改需求、添加功能的时候一步一步优化架构，不要想着一步到位。</p>
</li>
<li>
<p>分离，再分离</p>
<p>这应该是设计模式里面经常提及的内容，架构设计之根本。</p>
<p>MVVM 中每一层都可以细分出更小的层，当某一层代码超过了一定行数（我觉得200行就够了），就要考虑一下是否可以细分出更小的层。</p>
<p>比如把 Table View 的 Data Source 分离，把 View Model 中的事件处理与 Model 操作分离。如果某一部分过于庞大，MVVM 也就失去了意义。</p>
</li>
<li>
<p>没有最好的，只有最合适的</p>
<p>没有一个能称得上是绝对好的架构，不同应用有着不同的场景，合适的才是最好的。</p>
<p>比如有时候 <a href="http://www.objc.io/issue-13/viper.html">VIPER</a> 很合适大型应用，但是如果应用很简单，那绝对是浪费脑细胞。</p>
</li>
<li>
<p>ReactiveCocoa</p>
<p>所有谈到 iOS MVVM 的文章没有不提及 ReactiveCocoa 的，我也研究过，但是后来放弃了。放弃的原因是我觉得自己才入门，应该从基础的东西一点点学起。就像学 Javascript 的时候不直接用 JQuery 一样。</p>
<p>可能以后我会在自己的私人项目中尝试一下它~</p>
</li>
<li>
<p>多看别人代码</p>
<p>很多牛人在这些问题上已经走在了前面，他们的经验值得好好学习，能少走不少弯路。</p>
</li>
</ul>
<p>参考文章：</p>
<ul>
<li><a href="http://www.objc.io/issue-1/">Lighter View Controllers</a></li>
<li><a href="http://www.objc.io/issue-13/mvvm.html">Introduction to MVVM</a></li>
<li><a href="http://jpellat.com/how-to-separate-view-controllers-from-his-view-logic/">How to separate view controllers from their view logic</a></li>
</ul>
        </section>
        <div class="post-nav">
    		<!-- Duoshuo Comment BEGIN -->
<div class="ds-thread"></div>
<script type="text/javascript">
	var duoshuoQuery = {short_name:"zqqf16"};
	(function() {
		var ds = document.createElement('script');
		ds.type = 'text/javascript';ds.async = true;
		ds.src = 'http://static.duoshuo.com/embed.js';
		ds.charset = 'UTF-8';
		(document.getElementsByTagName('head')[0]
		|| document.getElementsByTagName('body')[0]).appendChild(ds);
	})();
</script>
<!-- Duoshuo Comment END -->

    	</div>
    </div>

</article>

        </section>
    </div>
    <footer class="main-footer">
        <div class="vertical-container">
            <div class="wrapper">
                <p class="copy">&copy; zqqf16</p>
            </div>
        </div>
    </footer>
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
      ga('create', 'UA-41282906-2', 'auto');
      ga('require', 'displayfeatures');
      ga('send', 'pageview');
    </script>
</body>
</html>
